package com.fourmob.datetimepicker.date;

import com.fourmob.datetimepicker.ResourceTable;
import com.fourmob.datetimepicker.Utils;
import com.fourmob.datetimepicker.util.RecordDateUtil;

import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.resource.ResourceManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.TouchEvent;

import java.security.InvalidParameterException;
import java.sql.Time;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Locale;

public class SimpleMonthView extends Component implements Component.DrawTask, Component.TouchEventListener {
    public static final String VIEW_PARAMS_HEIGHT = "height";
    public static final String VIEW_PARAMS_MONTH = "month";
    public static final String VIEW_PARAMS_YEAR = "year";
    public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day";
    public static final String VIEW_PARAMS_WEEK_START = "week_start";
    public static final String VIEW_PARAMS_NUM_DAYS = "num_days";
    public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month";
    public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num";
    protected static final int DEFAULT_NUM_ROWS = 6;
    protected static final int DEFAULT_HEIGHT = 48; // 32
    protected  int DAY_SELECTED_CIRCLE_SIZE;
    protected static final int DAY_SEPARATOR_WIDTH = 1;
    protected  int MINI_DAY_NUMBER_TEXT_SIZE;
    protected static final int MIN_HEIGHT = 10;
    protected  int MONTH_DAY_LABEL_TEXT_SIZE;
    protected  int MONTH_HEADER_SIZE;
    protected  int MONTH_LABEL_TEXT_SIZE;

    protected static float mScale = 0.0F;
    private static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x000110, "DatePickerDialog2");
    private static final int SELECTED_CIRCLE_ALPHA = 60;

    protected int mPadding = 0;
    protected Paint mMonthDayLabelPaint;
    protected Paint mMonthNumPaint;
    protected Paint mMonthTitleBGPaint;
    protected Paint mMonthTitlePaint;
    protected Paint mSelectedCirclePaint;
    protected int mDayTextColor;
    protected int mMonthTitleBGColor;
    protected int mMonthTitleColor;
    protected int mTodayNumberColor;
    protected int mFirstJulianDay = -1;
    protected int mFirstMonth = -1;
    protected int mLastMonth = -1;
    protected boolean mHasToday = false;
    protected int mSelectedDay = -1;
    protected int mToday = -1;
    protected int mWeekStart = 1;
    protected int mNumDays = 7;
    protected int mNumCells = mNumDays;
    protected int mSelectedLeft = -1;
    protected int mSelectedRight = -1;
    protected int mMonth;
    protected int mRowHeight = DEFAULT_HEIGHT;
    protected int mWidth;
    protected int mYear;
    private int mDayOfWeekStart = 0;
    private StringBuilder mStringBuilder;
	private Calendar mCalendar;
	private Calendar mDayLabelCalendar;
    private DateFormatSymbols mDateFormatSymbols = new DateFormatSymbols();
    private OnDayClickListener mOnDayClickListener;
    private float pointDown = -2000000;
    private float pointUp = -2000001;

    public SimpleMonthView(Context context) {
        super(context);
        mDayLabelCalendar = Calendar.getInstance();
        mCalendar = Calendar.getInstance();
        ResourceManager resources = context.getResourceManager();
        try {
            mDayTextColor = resources.getElement(ResourceTable.Color_date_picker_text_normal).getColor();
            mTodayNumberColor = resources.getElement(ResourceTable.Color_blue).getColor();
            mMonthTitleColor = resources.getElement(ResourceTable.Color_white).getColor();
            mMonthTitleBGColor = resources.getElement(ResourceTable.Color_circle_background).getColor();
        } catch (Exception e) {
            e.printStackTrace();
        }
        mStringBuilder = new StringBuilder(50);
        MINI_DAY_NUMBER_TEXT_SIZE = 48;
        MONTH_LABEL_TEXT_SIZE = 40;
        MONTH_DAY_LABEL_TEXT_SIZE = 32;
        MONTH_HEADER_SIZE = 150;
        DAY_SELECTED_CIRCLE_SIZE = 48;
        mRowHeight = (680 - MONTH_HEADER_SIZE) / 6;
        initView();
    }

	private int calculateNumRows() {
        int offset = findDayOffset();
        int dividend = (offset + mNumCells) / mNumDays;
        int remainder = (offset + mNumCells) % mNumDays;
        return dividend + (remainder > 0 ? 1 : 0);
	}

	private void drawMonthDayLabels(Canvas canvas) {
        int y = MONTH_HEADER_SIZE - (MONTH_DAY_LABEL_TEXT_SIZE / 2);
        int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
        HiLog.info(label,"SMVdrawMonthDayLabel data " + mNumDays + " , "
            + "mWidth:  " + mWidth + ", mPadding: " + mPadding);
        for (int i = 0; i < mNumDays; i++) {
            int calendarDay = (i + mWeekStart) % mNumDays;
            int x = (2 * i + 1) * dayWidthHalf + mPadding;
            mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
            HiLog.info(label,"SMVdrawMonthDayLabel position " + x + "y: " + y + " content: "
                + mDateFormatSymbols.getShortWeekdays()[mDayLabelCalendar.get(
                    Calendar.DAY_OF_WEEK)].toUpperCase(Locale.getDefault()));
            canvas.drawText(mMonthDayLabelPaint,mDateFormatSymbols.getShortWeekdays()[
                mDayLabelCalendar.get(Calendar.DAY_OF_WEEK)].toUpperCase(Locale.getDefault()), x, y);
        }
	}

	private void drawMonthTitle(Canvas canvas) {
        int x = (mWidth + 2 * mPadding) / 2;
        int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + (MONTH_LABEL_TEXT_SIZE / 3);
        HiLog.info(label,"SMVdrawMonthTitle position " + x + "y: " + y + " content: " + getMonthAndYearString());
        canvas.drawText(mMonthTitlePaint,getMonthAndYearString(), x, y);
	}

	private int findDayOffset() {
        return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
                - mWeekStart;
	}

	private String getMonthAndYearString() {
        mStringBuilder.setLength(0);
        Date date = mCalendar.getTime();
        String dateStr = date.toString();
        String[] ab = dateStr.split(" ");
        HiLog.info(label,"SMVgetMonthAndYearString() " + date.toString() + ": " + dateStr);
        return (ab.length > 0 ? ab[ab.length - 1] : "") + "年" + Utils.getSpellInMonth(date.getMonth());
    }

	private void onDayClick(SimpleMonthAdapter.CalendarDay calendarDay) {
		if (mOnDayClickListener != null) {
			mOnDayClickListener.onDayClick(this, calendarDay);
        }
	}

	private boolean sameDay(int monthDay, Time time) {
		return (mYear == time.getTime()) && (mMonth == time.getMonth()) && (monthDay == time.getDay());
	}

	protected void drawMonthNums(Canvas canvas) {
		int y = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH + MONTH_HEADER_SIZE;
		int paddingDay = (mWidth - 2 * mPadding) / (2 * mNumDays);
		int dayOffset = findDayOffset();
		int day = 1;
        HiLog.info(label,"SMVdrawMonthNums3 " + mNumCells + " mSelectedDay " + mSelectedDay
            + ", day " + day);
		while (day <= mNumCells) {
			int x = paddingDay * (1 + dayOffset * 2) + mPadding;
			if (mSelectedDay == day) {
			    mSelectedCirclePaint.setColor(new Color(Color.getIntColor("#c5e4ef")));
				canvas.drawCircle(x, y - MINI_DAY_NUMBER_TEXT_SIZE / 3, DAY_SELECTED_CIRCLE_SIZE, mSelectedCirclePaint);
            }
            if (mHasToday && (mToday == day)) {
				mMonthNumPaint.setColor(new Color(mTodayNumberColor));
            } else {
				mMonthNumPaint.setColor(new Color(mDayTextColor));
            }
            HiLog.info(label,"SMVdrawMonthNums position " + x + "y: " + y + " content: " + String.format("%d", day));
            canvas.drawText(mMonthNumPaint,String.format("%d", day), x, y);
			dayOffset++;
			if (dayOffset == mNumDays) {
				dayOffset = 0;
				y += mRowHeight;
			}
			day++;
		}
	}

	public SimpleMonthAdapter.CalendarDay getDayFromLocation(float x, float y) {
		int padding = mPadding;
		if ((x < padding) || (x > mWidth - mPadding)) {
			return null;
		}

		int yDay = (int) (y - MONTH_HEADER_SIZE) / mRowHeight;
		int day = 1 + ((int) ((x - padding) * mNumDays / (mWidth - padding - mPadding))
                    - findDayOffset()) + yDay * mNumDays;
		return new SimpleMonthAdapter.CalendarDay(mYear, mMonth, day);
	}

	protected void initView() {
        mMonthTitlePaint = new Paint();
        mMonthTitlePaint.setFakeBoldText(true);
        mMonthTitlePaint.setAntiAlias(true);
        mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE);
        mMonthTitlePaint.setColor(new Color(mDayTextColor));
        mMonthTitlePaint.setTextAlign(TextAlignment.CENTER); // Align.CENTER
        mMonthTitlePaint.setStyle(Paint.Style.FILL_STYLE);

        mMonthTitleBGPaint = new Paint();
        mMonthTitleBGPaint.setFakeBoldText(true);
        mMonthTitleBGPaint.setAntiAlias(true);
        mMonthTitleBGPaint.setColor(new Color(mMonthTitleBGColor));
        mMonthTitleBGPaint.setTextAlign(TextAlignment.CENTER); // Align.CENTER
        mMonthTitleBGPaint.setStyle(Paint.Style.FILL_STYLE);

        mSelectedCirclePaint = new Paint();
        mSelectedCirclePaint.setFakeBoldText(true);
        mSelectedCirclePaint.setAntiAlias(true);
        mSelectedCirclePaint.setColor(new Color(mTodayNumberColor));
        mSelectedCirclePaint.setTextAlign(TextAlignment.CENTER); // Align.CENTER
        mSelectedCirclePaint.setStyle(Paint.Style.FILL_STYLE);
        mSelectedCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA);

        mMonthDayLabelPaint = new Paint();
        mMonthDayLabelPaint.setAntiAlias(true);
        mMonthDayLabelPaint.setTextSize(MONTH_DAY_LABEL_TEXT_SIZE);
        mMonthDayLabelPaint.setColor(new Color(mDayTextColor));
        mMonthDayLabelPaint.setStyle(Paint.Style.FILL_STYLE);
        mMonthDayLabelPaint.setTextAlign(TextAlignment.CENTER);
        mMonthDayLabelPaint.setFakeBoldText(true);

        mMonthNumPaint = new Paint();
        mMonthNumPaint.setAntiAlias(true);
        mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE);
        mMonthNumPaint.setStyle(Paint.Style.FILL_STYLE);
        mMonthNumPaint.setTextAlign(TextAlignment.CENTER); // Align.CENTER
        mMonthNumPaint.setFakeBoldText(false);
        addDrawTask(this);
        setTouchEventListener(this::onTouchEvent);
	}

    @Override
    protected void setEstimateSizeListener(EstimateSizeListener listener) {
        super.setEstimateSizeListener(listener);
    }

	public void reuse() {
        postLayout();
	}

	public void setMonthParams(HashMap<String, Integer> params) {
        if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) {
            throw new InvalidParameterException("You must specify month and year for this view");
        }
		setTag(params);
        if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
            mRowHeight = params.get(VIEW_PARAMS_HEIGHT);
            if (mRowHeight < MIN_HEIGHT) {
                mRowHeight = MIN_HEIGHT;
            }
        }
        if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) {
            mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY);
        }
        mMonth = params.get(VIEW_PARAMS_MONTH);
        mYear = params.get(VIEW_PARAMS_YEAR);

        final java.sql.Time today = new Time(System.currentTimeMillis());
        today.setTime(System.currentTimeMillis());
        mHasToday = false;
        mToday = -1;
        HiLog.info(label, "DPV_setMonthParams " + mMonth + ", year : " + mYear + "");
		mCalendar.set(Calendar.MONTH, mMonth);
		mCalendar.set(Calendar.YEAR, mYear);
		mCalendar.set(Calendar.DAY_OF_MONTH, 1);
		mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);
        HiLog.info(label, "DPV_setMonthParams lookme ");
        if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
            mWeekStart = params.get(VIEW_PARAMS_WEEK_START);
        } else {
            mWeekStart = mCalendar.getFirstDayOfWeek();
        }
        HiLog.info(label, "DPV_setMonthParams year " + mYear + ", month " + mMonth);
        mNumCells = Utils.getDaysInMonth(mMonth, mYear);
        for (int i = 0; i < mNumCells; i++) {
            final int day = i + 1;
            if (sameDay(day, today)) {
                mHasToday = true;
                mToday = day;
            }
        }

	}

	public void setOnDayClickListener(OnDayClickListener onDayClickListener) {
		mOnDayClickListener = onDayClickListener;
	}

    @Override
    public void onDraw(Component component, Canvas canvas) {
       mWidth = component.getWidth();
        drawMonthTitle(canvas);
        drawMonthDayLabels(canvas);
        drawMonthNums(canvas);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
	    HiLog.info(label,"onTouchEvent " + event.getAction());
	    if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
	        pointDown = event.getPointerPosition(0).getY();
        }

        if (event.getAction() == TouchEvent.OTHER_POINT_UP) {
            HiLog.info(label,"onTouchEvent " + event.getAction());
        }
        if (event.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            HiLog.info(label,"onTouchEvent " + event.getAction());
            pointUp = event.getPointerPosition(0).getY();
            HiLog.info(label,"SMVonTouchEvent " + pointDown + ", pointUp " + pointUp);
            if (RecordDateUtil.isCloseDay() == true) {
                if (pointDown == pointUp) { //没有滚动
                    SimpleMonthAdapter.CalendarDay calendarDay = getDayFromLocation(event.getPointerPosition(0).getX(),
                        event.getPointerPosition(0).getY());
                    if (calendarDay != null) {
                        onDayClick(calendarDay);
                    }
                    RecordDateUtil.setHasScroll(false);
                } else {
                    SimpleMonthAdapter.CalendarDay calendarDay = getDayFromLocation(event.getPointerPosition(0).getX(),
                        event.getPointerPosition(0).getY());
                    if (calendarDay != null) {
                        onDayClick(calendarDay);
                    }
                    RecordDateUtil.setHasScroll(true);
                }
            } else {
                SimpleMonthAdapter.CalendarDay calendarDay = getDayFromLocation(event.getPointerPosition(0).getX(),
                    event.getPointerPosition(0).getY());
                if (calendarDay != null) {
                    onDayClick(calendarDay);
                }
            }
        }
        return true;
    }

    public static abstract interface OnDayClickListener {
		public abstract void onDayClick(SimpleMonthView simpleMonthView,
                                        SimpleMonthAdapter.CalendarDay calendarDay);
	}
}